home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / vim_src.zip / OPS.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  26KB  |  1,236 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMitation
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * ops.c: implementation of various operators: doshift, dodelete, dotilde,
  13.  *          dochange, doyank, doput, dojoin
  14.  */
  15.  
  16. #include "vim.h"
  17. #include "globals.h"
  18. #include "proto.h"
  19. #include "param.h"
  20. #include "ops.h"
  21.  
  22. /*
  23.  * We have one yank buffer for normal yanks and puts, nine yank buffers for
  24.  * deletes and 26 yank buffers for use by name.
  25.  * Each yank buffer is an array of pointers to lines.
  26.  */
  27. static struct yankbuf
  28. {
  29.     char        **y_array;        /* pointer to array of line pointers */
  30.     linenr_t     y_size;         /* number of lines in y_array */
  31.     char        y_type;         /* MLINE, MCHAR or MBLOCK */
  32. } y_buf[36];                    /* 0..9 = number buffers, 10..35 = char buffers */
  33.  
  34. static struct    yankbuf *y_current;        /* ptr to current yank buffer */
  35. static int        yankappend;                /* TRUE when appending */
  36. static struct    yankbuf *y_previous = NULL; /* ptr to last written yank buffer */
  37.  
  38. static void        get_yank_buffer __ARGS((int));
  39. static int        stuff_yank __ARGS((int, char *));
  40. static void        free_yank __ARGS((long));
  41. static void        free_yank_all __ARGS((void));
  42. static void        block_prep __ARGS((linenr_t, int));
  43.  
  44. /* variables use by block_prep, dodelete and doyank */
  45. static int        startspaces;
  46. static int        endspaces;
  47. static int        textlen;
  48. static char        *textstart;
  49. static colnr_t    textcol;
  50.  
  51. /*
  52.  * doshift - handle a shift operation
  53.  */
  54.     void
  55. doshift(op)
  56.     int             op;
  57. {
  58.     register int i;
  59.  
  60.     if (!u_save((linenr_t)(Curpos.lnum - 1), (linenr_t)(Curpos.lnum + nlines)))
  61.         return;
  62.  
  63.     Curpos.lnum += nlines;        /* start with last line, leave cursor on first */
  64.     for (i = nlines; --i >= 0; )
  65.         if (lineempty(--Curpos.lnum))
  66.             Curpos.col = 0;
  67.         else
  68.             shift_line(op == LSHIFT);
  69.  
  70.     updateScreen(CURSUPD);
  71.  
  72.     if (nlines > p_report)
  73.         smsg("%ld lines %ced", nlines, (op == RSHIFT) ? '>' : '<');
  74. }
  75.  
  76. /*
  77.  * shift the current line one shiftwidth left (if left != 0) or right
  78.  * leaves cursor on first blank in the line
  79.  */
  80.     void
  81. shift_line(left)
  82.     int left;
  83. {
  84.     register int count;
  85.     register int i, j;
  86.  
  87.     count = get_indent();            /* get current indent */
  88.  
  89.     if (p_sr)        /* round off indent */
  90.     {
  91.         i = count / p_sw;            /* compute new indent */
  92.         j = count % p_sw;
  93.         if (j)
  94.         {
  95.             if (!left)
  96.                 ++i;
  97.         }
  98.         else if (left)
  99.         {
  100.             if (i)
  101.                 --i;
  102.         }
  103.         else
  104.             ++i;
  105.         count = i * p_sw;
  106.     }
  107.     else                /* original vi indent */
  108.     {
  109.         if (left)
  110.         {
  111.             count -= p_sw;
  112.             if (count < 0)
  113.                 count = 0;
  114.         }
  115.         else
  116.             count += p_sw;
  117.     }
  118.     set_indent(count, TRUE);        /* set new indent */
  119. }
  120.  
  121. /*
  122.  * Set y_current and yankappend, according to the value of yankbuffer.
  123.  */
  124.     static void
  125. get_yank_buffer(writing)
  126.     int        writing;
  127. {
  128.     register int i;
  129.  
  130.     yankappend = FALSE;
  131.     if (yankbuffer == 0 && y_previous != NULL && !writing)
  132.     {
  133.         y_current = y_previous;
  134.         return;
  135.     }
  136.     i = yankbuffer;
  137.     if (isdigit(i))
  138.         i -= '0';
  139.     else if (islower(i))
  140.         i -= 'a' - 10;
  141.     else if (isupper(i))
  142.     {
  143.         i -= 'A' - 10;
  144.         yankappend = TRUE;
  145.     }
  146.     else            /* not 0-9, a-z or A-Z: use buffer 0 */
  147.         i = 0;
  148.     y_current = &(y_buf[i]);
  149.     if (writing)        /* remember the buffer we write into for doput() */
  150.         y_previous = y_current;
  151. }
  152.  
  153. /*
  154.  * (stop) recording into a yank buffer
  155.  */
  156.     int
  157. dorecord(c)
  158.     int c;
  159. {
  160.     char *p, *lp;
  161.     static int bufname;
  162.  
  163.     if (Recording == FALSE)         /* start recording */
  164.     {
  165.         if (!isalpha(c))
  166.             return FALSE;
  167.         Recording = TRUE;
  168.         showmode();
  169.         bufname = c;
  170.         return TRUE;
  171.     }
  172.     else                            /* stop recording */
  173.     {
  174.         Recording = FALSE;
  175.         if (p_mo)
  176.             msg("");
  177.         p = (char *)get_recorded();
  178.         if (p == NULL)
  179.             return FALSE;
  180.         lp = strrchr(p, 'v');    /* delete the trailing 'v' */
  181.         if (lp != NULL)
  182.             *lp = NUL;
  183.         return (stuff_yank(bufname, p));
  184.     }
  185. }
  186.  
  187. /*
  188.  * stuff string 'p' into yank buffer 'bufname' (append if uppercase)
  189.  * 'p' is assumed to be alloced.
  190.  */
  191.     static int
  192. stuff_yank(bufname, p)
  193.     int bufname;
  194.     char *p;
  195. {
  196.     char *lp;
  197.     char **pp;
  198.  
  199.     yankbuffer = bufname;
  200.     if (yankbuffer == '.')        /* read-only buffer */
  201.         return FALSE;
  202.     get_yank_buffer(TRUE);
  203.     if (yankappend && y_current->y_array != NULL)
  204.     {
  205.         pp = &(y_current->y_array[y_current->y_size - 1]);
  206.         lp = alloc((unsigned)(strlen(*pp) + strlen(p) + 1));
  207.         if (lp == NULL)
  208.         {
  209.             free(p);
  210.             return FALSE;
  211.         }
  212.         strcpy(lp, *pp);
  213.         strcat(lp, p);
  214.         free(p);
  215.         free(*pp);
  216.         *pp = lp;
  217.     }
  218.     else
  219.     {
  220.         free_yank_all();
  221.         if ((y_current->y_array = (char **)alloc((unsigned)sizeof(char *))) == NULL)
  222.         {
  223.             free(p);
  224.             return FALSE;
  225.         }
  226.         y_current->y_array[0] = p;
  227.         y_current->y_size = 1;
  228.         y_current->y_type = MCHAR;    /* used to be MLINE, why? */
  229.     }
  230.     return TRUE;
  231. }
  232.  
  233. /*
  234.  * execute a yank buffer: copy it into the stuff buffer
  235.  */
  236.     int
  237. doexecbuf(c)
  238.     int c;
  239. {
  240.     static int lastc = NUL;
  241.     long i;
  242.  
  243.     if (c == '@')            /* repeat previous one */
  244.         c = lastc;
  245.  
  246.     lastc = c;
  247.     if (!isalnum(c))        /* registers 0-9 and a-z are allowed */
  248.         return FALSE;
  249.  
  250.     yankbuffer = c;
  251.     get_yank_buffer(FALSE);
  252.     if (y_current->y_array == NULL)
  253.         return FALSE;
  254.  
  255.     for (i = y_current->y_size; --i >= 0; )
  256.     {
  257.     /* insert newline between lines and after last line if type is MLINE */
  258.         if (y_current->y_type == MLINE || i < y_current->y_size - 1)
  259.         {
  260.             if (ins_mapbuf("\n") < 0)
  261.                 return FALSE;
  262.         }
  263.         if (ins_mapbuf(y_current->y_array[i]) < 0)
  264.             return FALSE;
  265.     }
  266.  
  267.     return TRUE;
  268. }
  269.  
  270. /*
  271.  * insert a yank buffer: copy it into the Read buffer
  272.  */
  273.     int
  274. insertbuf(c)
  275.     int c;
  276. {
  277.     long i;
  278.  
  279.     if (!isalnum(c))        /* registers 0-9 and a-z are allowed */
  280.         return FALSE;
  281.  
  282.     yankbuffer = c;
  283.     get_yank_buffer(FALSE);
  284.     if (y_current->y_array == NULL)
  285.         return FALSE;
  286.  
  287.     for (i = 0; i < y_current->y_size; ++i)
  288.     {
  289.         stuffReadbuff(y_current->y_array[i]);
  290.     /* insert newline between lines and after last line if type is MLINE */
  291.         if (y_current->y_type == MLINE || i < y_current->y_size - 1)
  292.             stuffReadbuff("\n");
  293.     }
  294.     return TRUE;
  295. }
  296.  
  297. /*
  298.  * dodelete - handle a delete operation
  299.  */
  300.     void
  301. dodelete()
  302. {
  303.     register int    n;
  304.     linenr_t        lnum;
  305.     char            *ptr;
  306.  
  307.     /*
  308.      * Do a yank of whatever we're about to delete. If there's too much stuff
  309.      * to fit in the yank buffer, then get a confirmation before doing the
  310.      * delete. This is crude, but simple. And it avoids doing a delete of
  311.      * something we can't put back if we want.
  312.      */
  313.     if (yankbuffer == 0)                /* normal delete: shift number buffers */
  314.     {
  315.         y_current = &y_buf[9];
  316.         free_yank_all();                /* free buffer nine */
  317.         for (n = 9; n > 1; --n)
  318.             y_buf[n] = y_buf[n - 1];
  319.         y_previous = y_current = &y_buf[1];
  320.         y_buf[1].y_array = NULL;        /* set buffer one to empty */
  321.     }
  322.     else if (yankbuffer == '.')            /* read-only buffer */
  323.     {
  324.         beep();
  325.         return;
  326.     }
  327.     else                                /* yank into specified buffer */
  328.         get_yank_buffer(TRUE);
  329.  
  330.     if (!doyank(TRUE))
  331.     {
  332.         if (ask_yesno("cannot yank; delete anyway") != 'y')
  333.         {
  334.             emsg(e_abort);
  335.             return;
  336.         }
  337.     }
  338.  
  339.     if (!u_save((linenr_t)(startop.lnum - 1), (linenr_t)(endop.lnum + 1)))
  340.         return;
  341.  
  342. /*
  343.  * block mode
  344.  */
  345.     if (Quote_block)
  346.     {
  347.         for (lnum = Curpos.lnum; Curpos.lnum <= endop.lnum; ++Curpos.lnum)
  348.         {
  349.             block_prep(Curpos.lnum, TRUE);
  350.             if (textlen == 0)        /* nothing to delete */
  351.                 continue;
  352.  
  353.         /*
  354.          * If we delete a TAB, it may be replaced by several characters.
  355.          * Thus the number of characters may increase!
  356.          */
  357.             n = textlen - startspaces - endspaces;
  358.         /* number of characters increases - make room */
  359.             if (n < 0 && !canincrease(-n))
  360.                 continue;
  361.             ptr = nr2ptr(Curpos.lnum) + textcol;
  362.         /* copy the part after the deleted part */
  363.             memmove(ptr + startspaces + endspaces, ptr + textlen, strlen(ptr + textlen) + 1);
  364.         /* insert spaces */
  365.             copy_spaces(ptr, (size_t)(startspaces + endspaces));
  366.             if (n > 0)
  367.                 canincrease(0);
  368.         }
  369.         Curpos.lnum = lnum;
  370.         CHANGED;
  371.         updateScreen(VALID_TO_CURSCHAR);
  372.         nlines = 0;        /* no lines deleted */
  373.     }
  374.     else if (mtype == MLINE)
  375.     {
  376.         u_clearline();    /* "U" command should not be possible after "dd" */
  377.         if (operator == CHANGE)
  378.         {
  379.             dellines((long)(nlines - 1), TRUE);
  380.             Curpos.col = 0;
  381.             while (delchar(TRUE));
  382.         }
  383.         else
  384.         {
  385.             dellines(nlines, TRUE);
  386.         }
  387.     }
  388.     else if (nlines == 1)        /* del. within line */
  389.     {
  390.         n = endop.col - startop.col + 1 - oneless;
  391.         while (n--)
  392.             if (!delchar(TRUE))
  393.                 break;
  394.     }
  395.     else                        /* del. between lines */
  396.     {
  397.         n = Curpos.col;
  398.         while (Curpos.col >= n)
  399.             if (!delchar(TRUE))
  400.                 break;
  401.  
  402.         startop = Curpos;        /* remember Curpos */
  403.         ++Curpos.lnum;
  404.         dellines((long)(nlines - 2), TRUE);
  405.         n = endop.col - oneless;
  406.  
  407.         while (n-- >= 0)
  408.             if (!delchar(TRUE))
  409.                 break;
  410.         Curpos = startop;        /* restore Curpos */
  411.         dojoin(FALSE);
  412.     }
  413.  
  414.     if ((mtype == MCHAR && nlines <= 2 /* && p_nu == FALSE */) || operator == CHANGE)
  415.     {
  416.         cursupdate();
  417.         updateline();
  418.     }
  419.     else
  420.         updateScreen(CURSUPD);
  421.  
  422.     msgmore(-nlines);
  423. }
  424.  
  425. /*
  426.  * dotilde - handle the (non-standard vi) tilde operator
  427.  */
  428.     void
  429. dotilde()
  430. {
  431.         if (!u_save((linenr_t)(startop.lnum - 1), (linenr_t)(endop.lnum + 1)))
  432.                 return;
  433.  
  434.         if (Quote_block)        /* block mode */
  435.         {
  436.             for (; startop.lnum <= endop.lnum; ++startop.lnum)
  437.             {
  438.                 block_prep(startop.lnum, FALSE);
  439.                 startop.col = textcol;
  440.                 for (; --textlen >= 0; inc(&startop))
  441.                     swapchar(&startop);
  442.             }
  443.         }
  444.         else            /* not block mode */
  445.         {
  446.             if (mtype == MLINE)
  447.             {
  448.                     startop.col = 0;
  449.                     endop.col = strlen(nr2ptr(endop.lnum));
  450.                     if (endop.col)
  451.                             --endop.col;
  452.             }
  453.             else if (oneless)
  454.                 dec(&endop);
  455.  
  456.             for ( ; ltoreq(startop, endop); inc(&startop))
  457.                 swapchar(&startop);
  458.         }
  459.  
  460.         if (mtype == MCHAR && nlines == 1 && !Quote_block)
  461.         {
  462.             cursupdate();
  463.             updateline();
  464.         }
  465.         else
  466.             updateScreen(CURSUPD);
  467.  
  468.         if (nlines > p_report)
  469.                 smsg("%ld lines ~ed", nlines);
  470. }
  471.  
  472. /*
  473.  * If operator == UPPER: make uppercase,
  474.  * if operator == LOWER: make lowercase,
  475.  * else swap case of character at 'pos'
  476.  */
  477.     void
  478. swapchar(pos)
  479.     FPOS    *pos;
  480. {
  481.     int        c;
  482.  
  483.     c = gchar(pos);
  484.     if (operator == UPPER ? islower(c) :
  485.             (operator == LOWER ? isupper(c) :
  486.                 isalpha(c)))
  487.     {
  488.         pchar(*pos, c ^ 0x20);    /* Change current character */
  489.         CHANGED;
  490.     }
  491. }
  492.  
  493. /*
  494.  * dochange - handle a change operation
  495.  */
  496.     void
  497. dochange()
  498. {
  499.     register colnr_t            l;
  500.  
  501.     l = startop.col;
  502.  
  503.     dodelete();
  504.  
  505.     if ((l > Curpos.col) && !lineempty(Curpos.lnum))
  506.         incCurpos();
  507.  
  508.     startinsert(NUL, FALSE, (linenr_t)1);
  509. }
  510.  
  511. /*
  512.  * set all the yank buffers to empty (called from main())
  513.  */
  514.     void
  515. init_yank()
  516. {
  517.         register int i;
  518.  
  519.         for (i = 0; i < 36; ++i)
  520.                 y_buf[i].y_array = NULL;
  521. }
  522.  
  523. /*
  524.  * Free "n" lines from the current yank buffer.
  525.  * Called for normal freeing and in case of error.
  526.  */
  527.     static void
  528. free_yank(n)
  529.     long n;
  530. {
  531.     if (y_current->y_array != NULL)
  532.     {
  533.         register long i;
  534.  
  535.         for (i = n; --i >= 0; )
  536.         {
  537.             if (i % 1000 == 999)                    /* this may take a while */
  538.                 smsg("freeing %ld lines", i + 1);
  539.             free(y_current->y_array[i]);
  540.         }
  541.         free((char *)y_current->y_array);
  542.         y_current->y_array = NULL;
  543.         if (n >= 1000)
  544.             msg("");
  545.     }
  546. }
  547.  
  548.     static void
  549. free_yank_all()
  550. {
  551.         free_yank(y_current->y_size);
  552. }
  553.  
  554. /*
  555.  * Yank the text between Curpos and startpos into a yank buffer.
  556.  * If we are to append ("uppercase), we first yank into a new yank buffer and
  557.  * then concatenate the old and the new one (so we keep the old one in case
  558.  * of out-of-memory).
  559.  */
  560.     int
  561. doyank(deleting)
  562.     int deleting;
  563. {
  564.     long                 i;                /* index in y_array[] */
  565.     struct yankbuf        *curr;        /* copy of y_current */
  566.     struct yankbuf        new;         /* new yank buffer when appending */
  567.     char                **new_ptr;
  568.     register linenr_t    lnum;            /* current line number */
  569.     long                 j;
  570.  
  571.     char                *pnew;
  572.  
  573.     if (yankbuffer == '.')            /* read-only buffer */
  574.     {
  575.         beep();
  576.         return FALSE;
  577.     }
  578.     if (!deleting)                    /* dodelete() already set y_current */
  579.         get_yank_buffer(TRUE);
  580.  
  581.     curr = y_current;
  582.     if (yankappend && y_current->y_array != NULL) /* append to existing contents */
  583.         y_current = &new;
  584.     else
  585.         free_yank_all();        /* free previously yanked lines */
  586.  
  587.     y_current->y_size = nlines;
  588.     y_current->y_type = mtype;    /* set the yank buffer type */
  589.     y_current->y_array = (char **)alloc((unsigned)(sizeof(char *) * nlines));
  590.  
  591.     if (y_current->y_array == NULL)
  592.     {
  593.         y_current = curr;
  594.         return FALSE;
  595.     }
  596.  
  597.     i = 0;
  598.     lnum = startop.lnum;
  599.  
  600.     if (Quote_block)
  601.     {
  602. /*
  603.  * block mode
  604.  */
  605.         y_current->y_type = MBLOCK;    /* set the yank buffer type */
  606.         for ( ; lnum <= endop.lnum; ++lnum)
  607.         {
  608.             block_prep(lnum, FALSE);
  609.             if ((pnew= alloc(startspaces + endspaces + textlen + 1)) == NULL)
  610.                 goto fail;
  611.             y_current->y_array[i++] = pnew;
  612.             copy_spaces(pnew, (size_t)startspaces);
  613.             pnew += startspaces;
  614.             strncpy(pnew, textstart, (size_t)textlen);
  615.             pnew += textlen;
  616.             copy_spaces(pnew, (size_t)endspaces);
  617.             pnew += endspaces;
  618.             *pnew = NUL;
  619.         }
  620.     }
  621.     else
  622.     {
  623. /*
  624.  * there are three parts for non-block mode:
  625.  * 1. if mtype != MLINE yank last part of the top line
  626.  * 2. yank the lines between startop and endop, inclusive when mtype == MLINE
  627.  * 3. if mtype != MLINE yank first part of the bot line
  628.  */
  629.         if (mtype != MLINE)
  630.         {
  631.             if (nlines == 1)        /* startop and endop on same line */
  632.             {
  633.                     j = endop.col - startop.col + 1 - oneless;
  634.                     if ((y_current->y_array[0] = strnsave(nr2ptr(lnum) + startop.col, (int)j)) == NULL)
  635.                     {
  636.     fail:
  637.                             free_yank(i);    /* free the lines that we allocated */
  638.                             y_current = curr;
  639.                             return FALSE;
  640.                     }
  641.                     goto success;
  642.             }
  643.             if ((y_current->y_array[0] = strsave(nr2ptr(lnum++) + startop.col)) == NULL)
  644.                     goto fail;
  645.             ++i;
  646.         }
  647.  
  648.         while (mtype == MLINE ? (lnum <= endop.lnum) : (lnum < endop.lnum))
  649.         {
  650.             if ((y_current->y_array[i] = strsave(nr2ptr(lnum++))) == NULL)
  651.                     goto fail;
  652.             ++i;
  653.         }
  654.         if (mtype != MLINE)
  655.         {
  656.             if ((y_current->y_array[i] = strnsave(nr2ptr(endop.lnum), endop.col + 1 - oneless)) == NULL)
  657.                     goto fail;
  658.         }
  659.     }
  660.  
  661. success:
  662.     if (curr != y_current)        /* append the new block to the old block */
  663.     {
  664.         new_ptr = (char **)alloc((unsigned)(sizeof(char *) * (curr->y_size + y_current->y_size)));
  665.         if (new_ptr == NULL)
  666.                 goto fail;
  667.         for (j = 0; j < curr->y_size; ++j)
  668.                 new_ptr[j] = curr->y_array[j];
  669.         free(curr->y_array);
  670.         curr->y_array = new_ptr;
  671.  
  672.         if (mtype == MLINE)     /* MLINE overrides MCHAR and MBLOCK */
  673.                 curr->y_type = MLINE;
  674.         if (curr->y_type == MCHAR)        /* concatenate the last line of the old
  675.                                         block with the first line of the new block */
  676.         {
  677.                 new_ptr = (char **)alloc((unsigned)(strlen(curr->y_array[curr->y_size - 1]) + strlen(y_current->y_array[0]) + 1));
  678.                 if (new_ptr == NULL)
  679.                 {
  680.                         i = y_current->y_size - 1;
  681.                         goto fail;
  682.                 }
  683.                 strcpy((char *)new_ptr, curr->y_array[--j]);
  684.                 strcat((char *)new_ptr, y_current->y_array[0]);
  685.                 free(curr->y_array[j]);
  686.                 free(y_current->y_array[0]);
  687.                 curr->y_array[j++] = (char *)new_ptr;
  688.                 i = 1;
  689.         }
  690.         else
  691.                 i = 0;
  692.         while (i < y_current->y_size)
  693.                 curr->y_array[j++] = y_current->y_array[i++];
  694.         curr->y_size = j;
  695.         free(y_current->y_array);
  696.         y_current = curr;
  697.     }
  698.     if (operator == YANK && nlines > p_report)
  699.     {
  700.         cursupdate();        /* redisplay now, so message is not deleted */
  701.         smsg("%ld lines yanked", (long)(y_current->y_size));
  702.     }
  703.  
  704.     return TRUE;
  705. }
  706.  
  707.     void
  708. doput(dir, count)
  709.     int dir;
  710.     long count;
  711. {
  712.     char        *ptr, *ep;
  713.     int         newlen;
  714.     int            totlen;
  715.     linenr_t    lnum;
  716.     int            col;
  717.     long         i;        /* index in y_array[] */
  718.     int         y_type;
  719.     long         y_size;
  720.     char        **y_array;
  721.     long         nlines = 0;
  722.     int            vcol;
  723.     int            delchar;
  724.     int            incr = 0;
  725.     long        j;
  726.     FPOS        newCurpos;
  727.  
  728.     if (yankbuffer == '.')        /* use inserted text */
  729.     {
  730.         stuff_inserted(dir == FORWARD ? (count == -1 ? 'o' : 'a') : (count == -1 ? 'O' : 'i'), count, FALSE);
  731.         return;
  732.     }
  733.     get_yank_buffer(FALSE);
  734.  
  735.     y_type = y_current->y_type;
  736.     y_size = y_current->y_size;
  737.     y_array = y_current->y_array;
  738.  
  739.     if (count == -1)        /* :put command */
  740.     {
  741.         y_type = MLINE;
  742.         count = 1;
  743.     }
  744.  
  745.     if (y_size == 0 || y_array == NULL)
  746.     {
  747.         beep();
  748.         return;
  749.     }
  750.  
  751.     if (y_type == MBLOCK)
  752.     {
  753.         lnum = Curpos.lnum + y_size + 1;
  754.         if (lnum > line_count)
  755.             lnum = line_count + 1;
  756.         if (!u_save(Curpos.lnum - 1, lnum))
  757.             return;
  758.     }
  759.     else if (!u_saveCurpos())
  760.         return;
  761.  
  762.     newlen = strlen(y_array[0]);
  763.     CHANGED;
  764.  
  765.     lnum = Curpos.lnum;
  766.     col = Curpos.col;
  767.  
  768. /*
  769.  * block mode
  770.  */
  771.     if (y_type == MBLOCK)
  772.     {
  773.         if (dir == FORWARD && gcharCurpos() != NUL)
  774.         {
  775.             col = getvcol(&Curpos, 3) + 1;
  776.             ++Curpos.col;
  777.         }
  778.         else
  779.             col = getvcol(&Curpos, 2);
  780.         for (i = 0; i < y_size; ++i)
  781.         {
  782.             startspaces = 0;
  783.             endspaces = 0;
  784.             textcol = 0;
  785.             vcol = 0;
  786.             delchar = 0;
  787.  
  788.         /* add a new line */
  789.             if (Curpos.lnum > line_count)
  790.             {
  791.                 ep = alloc_line(0);
  792.                 if (ep == NULL)
  793.                         goto error;
  794.                 appendline(line_count, ep);
  795.                 ++nlines;
  796.             }
  797.             ptr = nr2ptr(Curpos.lnum);
  798.             while (vcol < col && *ptr)
  799.             {
  800.                 /* Count a tab for what it's worth (if list mode not on) */
  801.                 incr = chartabsize(*ptr, vcol);
  802.                 vcol += incr;
  803.                 ++ptr;
  804.                 ++textcol;
  805.             }
  806.             if (vcol < col)    /* line too short, padd with spaces */
  807.             {
  808.                 startspaces = col - vcol;
  809.             }
  810.             else if (vcol > col)
  811.             {
  812.                 endspaces = vcol - col;
  813.                 startspaces = incr - endspaces;
  814.                 --textcol;
  815.                 delchar = 1;
  816.             }
  817.             newlen = strlen(y_array[i]);
  818.             totlen = count * newlen + startspaces + endspaces;
  819.             if (!canincrease(totlen))
  820.                 break;
  821.             ptr = nr2ptr(Curpos.lnum) + textcol;
  822.  
  823.         /* move the text after the cursor to the end of the line. */
  824.             memmove(ptr + totlen - delchar, ptr, strlen(ptr) + 1);
  825.         /* may insert some spaces before the new text */
  826.             copy_spaces(ptr, (size_t)startspaces);
  827.             ptr += startspaces;
  828.         /* insert the new text */
  829.             for (j = 0; j < count; ++j)
  830.             {
  831.                     strncpy(ptr, y_array[i], (size_t)newlen);
  832.                     ptr += newlen;
  833.             }
  834.         /* may insert some spaces after the new text */
  835.             copy_spaces(ptr, (size_t)endspaces);
  836.  
  837.             ++Curpos.lnum;
  838.             if (i == 0)
  839.                 Curpos.col += startspaces;
  840.         }
  841.         Curpos.lnum = lnum;
  842.         cursupdate();
  843.         updateScreen(VALID_TO_CURSCHAR);
  844.     }
  845.     else        /* not block mode */
  846.     {
  847.         if (y_type == MCHAR)
  848.         {
  849.     /* if type is MCHAR, FORWARD is the same as BACKWARD on the next character */
  850.             if (dir == FORWARD && gcharCurpos() != NUL)
  851.             {
  852.                 ++col;
  853.                 if (newlen)
  854.                     ++Curpos.col;
  855.             }
  856.             newCurpos = Curpos;
  857.         }
  858.         else if (dir == BACKWARD)
  859.     /* if type is MLINE, BACKWARD is the same as FORWARD on the previous line */
  860.             --lnum;
  861.  
  862. /*
  863.  * simple case: insert into current line
  864.  */
  865.         if (y_type == MCHAR && y_size == 1)
  866.         {
  867.             i = count * newlen;
  868.             if (!canincrease((int)i))
  869.                     return;                 /* alloc() will give error message */
  870.             ep = nr2ptr(lnum) + col;
  871.             memmove(ep + i, ep, strlen(ep) + 1);
  872.             Curpos.col += i - 1;        /* put cursor on last putted char */
  873.             for (i = 0; i < count; ++i)
  874.             {
  875.                     strncpy(ep, y_array[0], (size_t)newlen);
  876.                     ep += newlen;
  877.             }
  878.             updateline();
  879.         }
  880.         else
  881.         {
  882.             if (y_type == MCHAR)
  883.                 --y_size;
  884.             while (--count >= 0)
  885.             {
  886.                 i = 0;
  887.                 if (y_type == MCHAR)
  888.                 {
  889.                     /*
  890.                      * Split the current line in two at the insert position.
  891.                      * Append y_array[0] to first line.
  892.                      * Insert y_array[size - 1] in front of second line.
  893.                      */
  894.                     ptr = nr2ptr(lnum) + col;
  895.                     col = strlen(y_array[y_size]);
  896.                     ep = alloc_line((unsigned)(strlen(ptr) + col));
  897.                     if (ep == NULL)
  898.                         goto error;
  899.                     strcpy(ep, y_array[y_size]);
  900.                     strcat(ep, ptr);
  901.                     appendline(lnum, ep);            /* insert in second line */
  902.                     ++nlines;
  903.                     *ptr = NUL;
  904.                     Curpos.lnum = lnum;
  905.                     if (!canincrease(newlen))        /* lnum == Curpos.lnum! */
  906.                         goto error;
  907.                     strcat(nr2ptr(lnum), y_array[0]);/* append to first line */
  908.                     i = 1;
  909.                 }
  910.  
  911.                 while (i < y_size)
  912.                 {
  913.                     ep = save_line(y_array[i++]);
  914.                     if (ep == NULL)
  915.                         goto error;
  916.                     appendline(lnum++, ep);
  917.                     ++nlines;
  918.                 }
  919.                 if (y_type == MCHAR)
  920.                     ++lnum;     /* lnum is now number of line below inserted lines */
  921.             }
  922.  
  923.             if (y_type == MLINE)
  924.             {
  925.                 Curpos.col = 0;
  926.                 if (dir == FORWARD)
  927.                 {
  928.                     updateScreen(NOT_VALID);        /* recompute Botline */
  929.                     ++Curpos.lnum;
  930.                 }
  931.                     /* put cursor on first non-blank in last inserted line */
  932.                 beginline(TRUE);
  933.             }
  934.             else        /* put cursor on first inserted character */
  935.                 Curpos = newCurpos;
  936.  
  937. error:
  938.             updateScreen(CURSUPD);
  939.         }
  940.     }
  941.  
  942.     msgmore(nlines);
  943.     set_want_col = TRUE;
  944. }
  945.  
  946. /*
  947.  * display the contents of the yank buffers
  948.  */
  949.     void
  950. dodis()
  951. {
  952.     register int i, n;
  953.     register long j;
  954.     register char *p;
  955.     register struct yankbuf *yb;
  956.  
  957.     settmode(0);            /* set cooked mode so output can be halted */
  958.     for (i = -1; i < 36; ++i)
  959.     {
  960.         if (i == -1)
  961.         {
  962.             if (y_previous != NULL)
  963.                 yb = y_previous;
  964.             else
  965.                 yb = &(y_buf[0]);
  966.         }
  967.         else
  968.             yb = &(y_buf[i]);
  969.         if (yb->y_array != NULL)
  970.         {
  971.             if (i == -1)
  972.                 outstrn("pP");
  973.             else
  974.             {
  975.                 outchar('"');
  976.                 if (i < 10)
  977.                     outchar(i + '0');
  978.                 else
  979.                     outchar(i + 'a' - 10);
  980.             }
  981.             outchar(' ');
  982.  
  983.             n = Columns - 4;
  984.             for (j = 0; j < yb->y_size && n > 0; ++j)
  985.             {
  986.                 if (j)
  987.                 {
  988.                     outstrn("^J");
  989.                     n -= 2;
  990.                 }
  991.                 for (p = yb->y_array[j]; *p && n > 0; ++p)
  992.                 {
  993.                     outstrn(transchar(*p));
  994.                     n -= charsize(*p);
  995.                 }
  996.             }
  997.             outchar('\n');
  998.             flushbuf();
  999.         }
  1000.     }
  1001.     settmode(1);
  1002.     wait_return(TRUE);
  1003. }
  1004.  
  1005. /*
  1006.  * join 'count' lines (minimal 2), including u_save()
  1007.  */
  1008.     void
  1009. dodojoin(count, flag, redraw)
  1010.     long    count;
  1011.     int        flag;
  1012.     int        redraw;
  1013. {
  1014.     if (!u_save((linenr_t)(Curpos.lnum - 1), (linenr_t)(Curpos.lnum + count)))
  1015.         return;
  1016.  
  1017.     while (--count > 0)
  1018.         if (!dojoin(flag))
  1019.         {
  1020.                 beep();
  1021.                 break;
  1022.         }
  1023.  
  1024.     if (redraw)
  1025.         updateScreen(VALID_TO_CURSCHAR);
  1026. }
  1027.  
  1028.     int
  1029. dojoin(insert_space)
  1030.     int            insert_space;
  1031. {
  1032.     char        *curr;
  1033.     char        *next;
  1034.     char        *endcurr;
  1035.     int         currsize;        /* size of the current line */
  1036.     int         nextsize;        /* size of the next line */
  1037.     int            spaces;            /* number of spaces to insert */
  1038.     int            rows_to_del;    /* number of rows on screen to delete */
  1039.     linenr_t    t;
  1040.  
  1041.     if (Curpos.lnum == line_count)        /* on last line */
  1042.         return FALSE;
  1043.  
  1044.     rows_to_del = plines_m(Curpos.lnum, Curpos.lnum + 1);
  1045.     curr = nr2ptr(Curpos.lnum);
  1046.     currsize = strlen(curr);
  1047.     next = nr2ptr((linenr_t)(Curpos.lnum + 1));
  1048.     spaces = 0;
  1049.     if (insert_space)
  1050.     {
  1051.         skipspace(&next);
  1052.         spaces = 1;
  1053.         if (*next == ')' || currsize == 0)
  1054.             spaces = 0;
  1055.         else
  1056.         {
  1057.             endcurr = curr + currsize - 1;
  1058.             if (*endcurr == ' ' || *endcurr == TAB)
  1059.             {
  1060.                 spaces = 0;
  1061.                 if (currsize > 1)
  1062.                     --endcurr;
  1063.             }
  1064.             if (p_js && strchr(".!?", *endcurr) != NULL)
  1065.                 ++spaces;
  1066.         }
  1067.     }
  1068.     nextsize = strlen(next);
  1069.     if (!canincrease(nextsize + spaces))
  1070.         return FALSE;
  1071.  
  1072.     curr = nr2ptr(Curpos.lnum); /* canincrease() will have changed the pointer */
  1073.  
  1074.     while (spaces)
  1075.     {
  1076.         *(curr + currsize++) = ' ';
  1077.         --spaces;
  1078.     }
  1079.     strcpy(curr + currsize, next);
  1080.  
  1081.     /*
  1082.      * Delete the following line. To do this we move the cursor there
  1083.      * briefly, and then move it back.
  1084.      */
  1085.     t = Curpos.lnum;
  1086.     ++Curpos.lnum;
  1087.     dellines(1L, FALSE);
  1088.     Curpos.lnum = t;
  1089.  
  1090.     /*
  1091.      * the number of rows on the screen is reduced by the difference
  1092.      * in number of rows of the two old lines and the one new line
  1093.      */
  1094.     rows_to_del -= plines(Curpos.lnum);
  1095.     if (rows_to_del > 0)
  1096.         s_del(Cursrow, rows_to_del, TRUE);
  1097.  
  1098.     if (currsize == 0)
  1099.         Curpos.col = 0;
  1100.     else
  1101.     {
  1102.         Curpos.col = currsize - 1;
  1103.         oneright();     /* go to first char. of joined line */
  1104.     }
  1105.     CHANGED;
  1106.  
  1107.     return TRUE;
  1108. }
  1109.  
  1110. /*
  1111.  * implementation of the format operator 'V'
  1112.  */
  1113.     void
  1114. doformat()
  1115. {
  1116.         /* prepare undo and join the lines */
  1117.     dodojoin((long)endop.lnum - (long)startop.lnum + 1, TRUE, FALSE);
  1118.  
  1119.         /* put cursor on last non-space */
  1120.     coladvance(29999);
  1121.     while (Curpos.col && isspace(gcharCurpos()))
  1122.         decCurpos();
  1123.     curs_columns();            /* update Cursvcol */
  1124.  
  1125.         /* do the formatting */
  1126.     State = INSERT;        /* for Opencmd() */
  1127.     insertchar(NUL);
  1128.     State = NORMAL;
  1129.     updateScreen(NOT_VALID);
  1130. }
  1131.  
  1132.     void
  1133. startinsert(initstr, startln, count)
  1134.     int            initstr;
  1135.     int         startln;        /* if set, insert at start of line */
  1136.     long         count;
  1137. {
  1138.     Insstart = Curpos;
  1139.     if (startln)
  1140.         Insstart.col = 0;
  1141.  
  1142.     if (initstr != NUL)
  1143.     {
  1144.             ResetBuffers();
  1145.             AppendNumberToRedobuff(count);
  1146.             AppendToRedobuff(mkstr(initstr));
  1147.     }
  1148.  
  1149.     if (initstr == 'R')
  1150.         State = REPLACE;
  1151.     else
  1152.         State = INSERT;
  1153.  
  1154.     if (p_mo)
  1155.         showmode();
  1156.  
  1157.     edit(count);
  1158. }
  1159.  
  1160. /*
  1161.  * prepare a few things for block mode yank/delete/tilde
  1162.  *
  1163.  * for delete:
  1164.  * - textlen includes the first/last char to be (partly) deleted
  1165.  * - start/endspaces is the number of columns that are taken by the
  1166.  *     first/last deleted char minus the number of columns that have to be deleted.
  1167.  * for yank and tilde:
  1168.  * - textlen includes the first/last char to be wholly yanked
  1169.  * - start/endspaces is the number of columns of the first/last yanked char
  1170.  *   that are to be yanked.
  1171.  */
  1172.     static void
  1173. block_prep(lnum, delete)
  1174.     linenr_t    lnum;
  1175.     int            delete;
  1176. {
  1177.     int            vcol;
  1178.     int            incr = 0;
  1179.     char        *pend;
  1180.  
  1181.     startspaces = 0;
  1182.     endspaces = 0;
  1183.     textlen = 0;
  1184.     textcol = 0;
  1185.     vcol = 0;
  1186.     textstart = nr2ptr(lnum);
  1187.     while (vcol < startvcol && *textstart)
  1188.     {
  1189.         /* Count a tab for what it's worth (if list mode not on) */
  1190.         incr = chartabsize(*textstart, vcol);
  1191.         vcol += incr;
  1192.         ++textstart;
  1193.         ++textcol;
  1194.     }
  1195.     if (vcol < startvcol)    /* line too short */
  1196.     {
  1197.         if (!delete)
  1198.             endspaces = endvcol - startvcol + 1;
  1199.     }
  1200.     else /* vcol >= startvcol */
  1201.     {
  1202.         startspaces = vcol - startvcol;
  1203.         if (delete && vcol > startvcol)
  1204.             startspaces = incr - startspaces;
  1205.         pend = textstart;
  1206.         while (vcol <= endvcol && *pend)
  1207.         {
  1208.             /* Count a tab for what it's worth (if list mode not on) */
  1209.             incr = chartabsize(*pend, vcol);
  1210.             vcol += incr;
  1211.             ++pend;
  1212.         }
  1213.         if (vcol < endvcol && !delete)    /* line too short */
  1214.         {
  1215.             endspaces = endvcol - vcol;
  1216.         }
  1217.         else if (vcol > endvcol)
  1218.         {
  1219.             if (delete)
  1220.                 endspaces = vcol - endvcol - 1;
  1221.             else if (pend != textstart)
  1222.             {
  1223.                 endspaces = incr - (vcol - endvcol);
  1224.                 if (endspaces)
  1225.                     --pend;
  1226.             }
  1227.         }
  1228.         if (delete && startspaces)
  1229.         {
  1230.             --textstart;
  1231.             --textcol;
  1232.         }
  1233.         textlen = pend - textstart;
  1234.     }
  1235. }
  1236.